21.4.1 简单的 Hello World 插件#
插件实现#
// src/plugin.ts import { Plugin, PluginConfig, PluginContext } from '@claude-code/plugin-sdk';
export class HelloWorldPlugin extends Plugin { constructor() { super({ name: 'hello-world', version: '1.0.0', description: 'A simple Hello World plugin' }); }
async initialize(config: PluginConfig): Promise<void> { console.log('Hello World Plugin initialized'); }
async start(): Promise<void> { console.log('Hello World Plugin started'); }
async stop(): Promise<void> { console.log('Hello World Plugin stopped'); }
async cleanup(): Promise<void> { console.log('Hello World Plugin cleaned up'); } }
插件清单#
bashyaml # plugin.yaml name: hello-world version: 1.0.0 description: A simple Hello World plugin author: Your Name license: MIT main: dist/plugin.js types: dist/plugin.d.ts ### 使用示例 // 使用插件 import { HelloWorldPlugin } from './plugin'; const plugin = new HelloWorldPlugin(); // 初始化插件 await plugin.initialize({}); // 启动插件 await plugin.start(); // 获取插件状态 const status = plugin.getStatus(); console.log(status); // 停止插件 await plugin.stop(); // 清理插件 await plugin.cleanup();
21.4.2 带工具的插件#
插件实现#
bashtypescript // src/plugin.ts import { Plugin, PluginConfig, Tool, ToolResult, PluginContext } from '@claude-code/plugin-sdk'; import { GreetingTool } from './tools/greeting'; import { TimeTool } from './tools/time'; export class ToolsPlugin extends Plugin { private toolManager: any; constructor() { super({ name: 'tools-plugin', version: '1.0.0', description: 'A plugin with tools' }); this.toolManager = { register: (tool: Tool) => {}, execute: async (name: string, params: any, context: PluginContext) => { return { success: true, data: {} }; } }; } async initialize(config: PluginConfig): Promise<void> { console.log('Tools Plugin initialized'); // 注册工具 this.toolManager.register(new GreetingTool()); this.toolManager.register(new TimeTool()); } async start(): Promise<void> { console.log('Tools Plugin started'); } async stop(): Promise<void> { console.log('Tools Plugin stopped'); } async cleanup(): Promise<void> { console.log('Tools Plugin cleaned up'); } } ### Greeting 工具 // src/tools/greeting.ts import { Tool, ToolResult, PluginContext } from '@claude-code/plugin-sdk'; export class GreetingTool extends Tool { constructor() { super({ name: 'greeting', description: 'Generate a greeting message', parameters: [ { name: 'name', type: 'string', description: 'The name to greet', required: true }, { name: 'language', type: 'string', description: 'The language of the greeting', required: false, default: 'english' } ] }); } async execute(params: Record<string, any>, context: PluginContext): Promise<ToolResult> { const name = params.name; const language = params.language || 'english'; let greeting: string; switch (language.toLowerCase()) { case 'english': greeting = `Hello, ${name}!`; break; case 'spanish': greeting = `¡Hola, ${name}!`; break; case 'french': greeting = `Bonjour, ${name}!`; break; case 'german': greeting = `Hallo, ${name}!`; break; case 'chinese': greeting = `你好,${name}!`; break; default: greeting = `Hello, ${name}!`; } return { success: true, data: { greeting, language } }; } }
Time 工具#
bashtypescript // src/tools/time.ts import { Tool, ToolResult, PluginContext } from '@claude-code/plugin-sdk'; export class TimeTool extends Tool { constructor() { super({ name: 'time', description: 'Get current time in various formats', parameters: [ { name: 'format', type: 'string', description: 'The format of the time (iso, unix, readable)', required: false, default: 'iso' }, { name: 'timezone', type: 'string', description: 'The timezone (e.g., UTC, America/New_York)', required: false } ] }); } async execute(params: Record<string, any>, context: PluginContext): Promise<ToolResult> { const format = params.format || 'iso'; const timezone = params.timezone; let date: Date; if (timezone) { // 使用指定时区 date = new Date(); } else { date = new Date(); } let time: string; switch (format.toLowerCase()) { case 'iso': time = date.toISOString(); break; case 'unix': time = Math.floor(date.getTime() / 1000).toString(); break; case 'readable': time = date.toLocaleString(); break; default: time = date.toISOString(); } return { success: true, data: { time, format, timezone: timezone || 'local' } }; } } ### 使用示例 // 使用插件 import { ToolsPlugin } from './plugin'; const plugin = new ToolsPlugin(); // 初始化插件 await plugin.initialize({}); // 启动插件 await plugin.start(); // 执行工具 const greetingResult = await plugin.toolManager.execute( 'greeting', { name: 'World', language: 'chinese' }, {} ); console.log(greetingResult.data.greeting); // 你好,World! const timeResult = await plugin.toolManager.execute( 'time', { format: 'readable' }, {} ); console.log(timeResult.data.time); // 2024-01-15 10:30:00 // 停止插件 await plugin.stop(); // 清理插件 await plugin.cleanup();
21.4.3 带命令的插件#
插件实现#
bashtypescript // src/plugin.ts import { Plugin, PluginConfig, Command, CommandResult, PluginContext } from '@claude-code/plugin-sdk'; import { GreetCommand } from './commands/greet'; import { CalcCommand } from './commands/calc'; export class CommandsPlugin extends Plugin { private commandManager: any; constructor() { super({ name: 'commands-plugin', version: '1.0.0', description: 'A plugin with commands' }); this.commandManager = { register: (command: Command) => {}, execute: async (name: string, args: string[], context: PluginContext) => { return { success: true, output: '' }; } }; } async initialize(config: PluginConfig): Promise<void> { console.log('Commands Plugin initialized'); // 注册命令 this.commandManager.register(new GreetCommand()); this.commandManager.register(new CalcCommand()); } async start(): Promise<void> { console.log('Commands Plugin started'); } async stop(): Promise<void> { console.log('Commands Plugin stopped'); } async cleanup(): Promise<void> { console.log('Commands Plugin cleaned up'); } } ### Greet 命令 // src/commands/greet.ts import { Command, CommandResult, PluginContext } from '@claude-code/plugin-sdk'; export class GreetCommand extends Command { constructor() { super({ name: 'greet', description: 'Greet someone', parameters: [ { name: 'name', type: 'string', description: 'The name to greet', required: true, short: 'n' }, { name: 'formal', type: 'flag', description: 'Use formal greeting', required: false, short: 'f' } ] }); } async execute(args: string[], context: PluginContext): Promise<CommandResult> { const parsed = this.parseArgs(args); const name = parsed.name; const formal = parsed.formal || false; let greeting: string; if (formal) { greeting = `Good day, ${name}. It is a pleasure to meet you.`; } else { greeting = `Hey, ${name}! How's it going?`; } return { success: true, output: greeting }; } }
Calc 命令#
bashtypescript // src/commands/calc.ts import { Command, CommandResult, PluginContext } from '@claude-code/plugin-sdk'; export class CalcCommand extends Command { constructor() { super({ name: 'calc', description: 'Perform mathematical calculations', parameters: [ { name: 'expression', type: 'string', description: 'The mathematical expression to evaluate', required: true, short: 'e' }, { name: 'precision', type: 'number', description: 'Number of decimal places', required: false, default: 2, short: 'p' } ] }); } async execute(args: string[], context: PluginContext): Promise<CommandResult> { const parsed = this.parseArgs(args); const expression = parsed.expression; const precision = parsed.precision || 2; try { // 安全地评估表达式 const result = this.evaluateExpression(expression); // 格式化结果 const formatted = result.toFixed(precision); return { success: true, output: `${expression} = ${formatted}` }; } catch (error) { return { success: false, output: '', error: error.message, exitCode: 1 }; } } private evaluateExpression(expression: string): number { // 只允许数字和基本运算符 const sanitized = expression.replace(/[^0-9+\-*/().]/g, ''); // 使用 Function 构造函数安全地评估 return new Function(`return ${sanitized}`)(); } } ### 使用示例 // 使用插件 import { CommandsPlugin } from './plugin'; const plugin = new CommandsPlugin(); // 初始化插件 await plugin.initialize({}); // 启动插件 await plugin.start(); // 执行命令 const greetResult = await plugin.commandManager.execute( 'greet', ['--name', 'World', '--formal'], {} ); console.log(greetResult.output); // Good day, World. It is a pleasure to meet you. const calcResult = await plugin.commandManager.execute( 'calc', ['--expression', '2 + 2 * 3', '--precision', '0'], {} ); console.log(calcResult.output); // 2 + 2 * 3 = 8 // 停止插件 await plugin.stop(); // 清理插件 await plugin.cleanup();
21.4.4 带钩子的插件#
插件实现#
bashtypescript // src/plugin.ts import { Plugin, PluginConfig, Hook, HookResult, HookEvent, PluginContext } from '@claude-code/plugin-sdk'; import { LoggingHook } from './hooks/logging'; import { ErrorHandlingHook } from './hooks/error-handling'; export class HooksPlugin extends Plugin { private hookManager: any; constructor() { super({ name: 'hooks-plugin', version: '1.0.0', description: 'A plugin with hooks' }); this.hookManager = { register: (hook: Hook) => {}, execute: async (type: string, event: HookEvent, context: PluginContext) => { return { success: true }; } }; } async initialize(config: PluginConfig): Promise<void> { console.log('Hooks Plugin initialized'); // 注册钩子 this.hookManager.register(new LoggingHook()); this.hookManager.register(new ErrorHandlingHook()); } async start(): Promise<void> { console.log('Hooks Plugin started'); } async stop(): Promise<void> { console.log('Hooks Plugin stopped'); } async cleanup(): Promise<void> { console.log('Hooks Plugin cleaned up'); } } ### Logging 钩子 // src/hooks/logging.ts import { Hook, HookResult, HookEvent, PluginContext, HookType } from '@claude-code/plugin-sdk'; export class LoggingHook extends Hook { constructor() { super({ name: 'logging', type: 'before_command' as HookType, description: 'Log all commands before execution', priority: 10 }); } async execute(event: HookEvent, context: PluginContext): Promise<HookResult> { const command = event.data.command; const args = event.data.args; console.log(`[LoggingHook] Executing command: ${command} ${args.join(' ')}`); return { success: true }; } }
Error Handling 钩子#
bashtypescript // src/hooks/error-handling.ts import { Hook, HookResult, HookEvent, PluginContext, HookType } from '@claude-code/plugin-sdk'; export class ErrorHandlingHook extends Hook { constructor() { super({ name: 'error-handling', type: 'on_error' as HookType, description: 'Handle errors and provide helpful messages', priority: 100 }); } async execute(event: HookEvent, context: PluginContext): Promise<HookResult> { const error = event.data.error; console.error(`[ErrorHandlingHook] Error occurred: ${error.message}`); // 提供错误建议 const suggestions = this.getErrorSuggestions(error); if (suggestions.length > 0) { console.log('[ErrorHandlingHook] Suggestions:'); suggestions.forEach((suggestion, index) => { console.log(` ${index + 1}. ${suggestion}`); }); } return { success: true }; } private getErrorSuggestions(error: Error): string[] { const suggestions: string[] = []; if (error.message.includes('permission')) { suggestions.push('Check if you have the necessary permissions'); suggestions.push('Try running with elevated privileges'); } if (error.message.includes('not found')) { suggestions.push('Verify the file or resource exists'); suggestions.push('Check the spelling of the file name'); } if (error.message.includes('network')) { suggestions.push('Check your internet connection'); suggestions.push('Verify the server is running'); } return suggestions; } } ### 使用示例 // 使用插件 import { HooksPlugin } from './plugin'; const plugin = new HooksPlugin(); // 初始化插件 await plugin.initialize({}); // 启动插件 await plugin.start(); // 触发 before_command 钩子 await plugin.hookManager.execute( 'before_command', { type: 'before_command', data: { command: 'greet', args: ['--name', 'World'] }, timestamp: new Date() }, {} ); // 触发 on_error 钩子 await plugin.hookManager.execute( 'on_error', { type: 'on_error', data: { error: new Error('Permission denied') }, timestamp: new Date() }, {} ); // 停止插件 await plugin.stop(); // 清理插件 await plugin.cleanup();
21.4.5 完整的插件示例#
插件实现#
bashtypescript // src/plugin.ts import { Plugin, PluginConfig, Tool, Command, Hook, ToolResult, CommandResult, HookResult, HookEvent, PluginContext } from '@claude-code/plugin-sdk'; import { GreetingTool } from './tools/greeting'; import { TimeTool } from './tools/time'; import { GreetCommand } from './commands/greet'; import { CalcCommand } from './commands/calc'; import { LoggingHook } from './hooks/logging'; import { ErrorHandlingHook } from './hooks/error-handling'; export class CompletePlugin extends Plugin { private toolManager: any; private commandManager: any; private hookManager: any; private logger: any; constructor() { super({ name: 'complete-plugin', version: '1.0.0', description: 'A complete plugin with tools, commands, and hooks' }); this.toolManager = { register: (tool: Tool) => {}, execute: async (name: string, params: any, context: PluginContext) => { return { success: true, data: {} }; } }; this.commandManager = { register: (command: Command) => {}, execute: async (name: string, args: string[], context: PluginContext) => { return { success: true, output: '' }; } }; this.hookManager = { register: (hook: Hook) => {}, execute: async (type: string, event: HookEvent, context: PluginContext) => { return { success: true }; } }; this.logger = { info: (message: string) => console.log(`[INFO] ${message}`), error: (message: string) => console.error(`[ERROR] ${message}`) }; } async initialize(config: PluginConfig): Promise<void> { this.logger.info('Complete Plugin initialized'); // 注册工具 this.toolManager.register(new GreetingTool()); this.toolManager.register(new TimeTool()); // 注册命令 this.commandManager.register(new GreetCommand()); this.commandManager.register(new CalcCommand()); // 注册钩子 this.hookManager.register(new LoggingHook()); this.hookManager.register(new ErrorHandlingHook()); } async start(): Promise<void> { this.logger.info('Complete Plugin started'); } async stop(): Promise<void> { this.logger.info('Complete Plugin stopped'); } async cleanup(): Promise<void> { this.logger.info('Complete Plugin cleaned up'); } } ### 使用示例 // 使用插件 import { CompletePlugin } from './plugin'; const plugin = new CompletePlugin(); // 初始化插件 await plugin.initialize({}); // 启动插件 await plugin.start(); // 使用工具 const greetingResult = await plugin.toolManager.execute( 'greeting', { name: 'World', language: 'chinese' }, {} ); console.log(greetingResult.data.greeting); // 你好,World! // 使用命令 const greetResult = await plugin.commandManager.execute( 'greet', ['--name', 'World', '--formal'], {} ); console.log(greetResult.output); // Good day, World. It is a pleasure to meet you. // 触发钩子 await plugin.hookManager.execute( 'before_command', { type: 'before_command', data: { command: 'greet', args: ['--name', 'World'] }, timestamp: new Date() }, {} ); // 停止插件 await plugin.stop(); // 清理插件 await plugin.cleanup();
21.4.6 插件测试示例#
测试文件#
bashtypescript // __tests__/plugin.test.ts import { CompletePlugin } from '../src/plugin'; describe('CompletePlugin', () => { let plugin: CompletePlugin; beforeEach(() => { plugin = new CompletePlugin(); }); afterEach(async () => { try { await plugin.cleanup(); } catch (error) { // 忽略清理错误 } }); test('should initialize successfully', async () => { await expect(plugin.initialize({})).resolves.not.toThrow(); const status = plugin.getStatus(); expect(status.name).toBe('complete-plugin'); expect(status.version).toBe('1.0.0'); }); test('should start successfully', async () => { await plugin.initialize({}); await expect(plugin.start()).resolves.not.toThrow(); }); test('should stop successfully', async () => { await plugin.initialize({}); await plugin.start(); await expect(plugin.stop()).resolves.not.toThrow(); }); test('should cleanup successfully', async () => { await plugin.initialize({}); await plugin.start(); await plugin.stop(); await expect(plugin.cleanup()).resolves.not.toThrow(); }); }); ### 工具测试 // __tests__/tools/greeting.test.ts import { GreetingTool } from '../../src/tools/greeting'; describe('GreetingTool', () => { let tool: GreetingTool; beforeEach(() => { tool = new GreetingTool(); }); test('should generate English greeting', async () => { const result = await tool.execute( { name: 'World', language: 'english' }, {} ); expect(result.success).toBe(true); expect(result.data.greeting).toBe('Hello, World!'); }); test('should generate Chinese greeting', async () => { const result = await tool.execute( { name: 'World', language: 'chinese' }, {} ); expect(result.success).toBe(true); expect(result.data.greeting).toBe('你好,World!'); }); test('should validate parameters', () => { const result = tool.validate({}); expect(result.valid).toBe(false); expect(result.errors).toContain('Missing required parameter: name'); }); });
命令测试#
bashtypescript // __tests__/commands/greet.test.ts import { GreetCommand } from '../../src/commands/greet'; describe('GreetCommand', () => { let command: GreetCommand; beforeEach(() => { command = new GreetCommand(); }); test('should greet informally', async () => { const result = await command.execute( ['--name', 'World'], {} ); expect(result.success).toBe(true); expect(result.output).toContain('Hey, World!'); }); test('should greet formally', async () => { const result = await command.execute( ['--name', 'World', '--formal'], {} ); expect(result.success).toBe(true); expect(result.output).toContain('Good day, World.'); }); test('should parse arguments correctly', () => { const parsed = command.parseArgs(['--name', 'World', '--formal']); expect(parsed.name).toBe('World'); expect(parsed.formal).toBe(true); }); }); ### 运行测试 # 运行所有测试 npm test # 运行特定测试文件 npm test -- plugin.test.ts # 运行测试并生成覆盖率报告 npm test -- --coverage # 监听模式 npm run test:watch